package org.df.ulab.bot.plugins.pmi;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.df.ulab.bot.api.command.Command;
import org.df.ulab.bot.api.command.InitializationRequired;
import org.df.ulab.bot.api.command.MsgInterpreter;
import org.df.ulab.bot.api.communication.CommunicationService;
import org.df.ulab.bot.api.communication.CommunicationService.DefaultMsgs;
import org.df.ulab.bot.api.message.ImMessage;
import org.df.ulab.bot.api.message.ImStatusMessage;
import org.df.ulab.bot.api.message.Message;
import org.df.ulab.bot.api.message.MessageHandler;
import org.df.ulab.bot.plugins.pmi.commons.PmiDatabase;
import org.df.ulab.bot.plugins.pmi.commons.PmiSettings;
import org.df.ulab.bot.util.command.CommandClassLoader;
import org.df.ulab.bot.util.core.EngineSession;
import org.df.ulab.bot.util.core.StorageSystem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Daniel Filgueiras
 * @since 19/06/2011
 */
public abstract class AbstractPmiHandler implements MessageHandler {

	private static final Logger logger = LoggerFactory.getLogger(AbstractPmiHandler.class);
	
	protected final List<MsgInterpreter> interpreters = new ArrayList<MsgInterpreter>();
	
	protected Set<String> authorizedUsers = new HashSet<String>();
	protected Set<String> unauthorizedUsers = new HashSet<String>();
	protected Set<String> blockedUsers = new HashSet<String>();
	
	protected PmiDatabase db;
	protected boolean dbEnabled;
	protected PmiSettings settings;
	
	protected abstract PmiDatabase getDb();
	protected abstract String getIdentifier();
	
	@Override
	public void init(CommunicationService service) {

		String identifier = getIdentifier();
		logger.debug("Initializing {}", identifier);
		
		settings = StorageSystem.getInstance().load(PmiSettings.class, identifier, false);
		if (settings == null)
			return;
		
		CommandClassLoader loader = new CommandClassLoader(Thread.currentThread().getContextClassLoader());
		interpreters.addAll(loader.createInterpreters());
		
		HandlerSession.reset();
		HandlerSession.setPmtSettings(settings);
		HandlerSession.setCommandLoader(loader);
		
		try {
			db = getDb();
			HandlerSession.setDatabase(db);
			
			dbEnabled = db.connect();
		} catch (Exception e) {
			logger.warn("Unable to connect do database", e);
		}
	}
	
	@Override
	public void close() {
		logger.debug("Shutting down handler");
		db.close();
		db = null;
	}

	@Override
	public boolean handle(Message message) {
		
		if (!(message instanceof ImMessage))
			return false;
		
		
		Command command = null;
		for (MsgInterpreter interpreter : interpreters) {
			command = interpreter.parseMessage(message);
			if (command != null)
				break;
		}
		
		if (command == null) {
			return false;
		}
		
		CommunicationService service = EngineSession.getCommunicationService();
		
		if (!dbEnabled && command.getClass().isAnnotationPresent(InitializationRequired.class)) {
			service.reply(message, DefaultMsgs.Database_connection_is_not_estabilished);
			return true;
		}
		
		if (dbEnabled && !isAuthorizedUser((ImMessage)message)) {
			if (!(message instanceof ImStatusMessage))
				service.reply(message, DefaultMsgs.Unauthorize_User);
			return true;
		}
		
		try {
			if (!command.proccess(message)) {
				service.reply(message, DefaultMsgs.Command_Execution_Failed);	
			}
		} catch (Exception e) {
			service.reply(message, DefaultMsgs.Command_Execution_Failed);
			logger.error("Command Execution Failed",e);
		}
		
		return true;
	}
	
	protected boolean isAuthorizedUser(ImMessage message) {
		
		if (settings.isAutoLoginOnly()) {
			
			String remoteid = message.getRemoteId();
			
			if (authorizedUsers.contains(remoteid))
				return true;
			
			if (unauthorizedUsers.contains(remoteid))
				return false;
			
			if (isValidMember(remoteid)) {
				authorizedUsers.add(remoteid);
				return true;
			}
			
			if (!blockedUsers.contains(remoteid)) {
				logger.debug("Unsubscribe remoteid: {}", remoteid);
				CommunicationService service = EngineSession.getCommunicationService();
				service.unsubscribe(remoteid);
				blockedUsers.add(remoteid);
			}
			unauthorizedUsers.add(remoteid);
			return false;
		}
		
		return true;
	}
	
	protected boolean isValidMember(String remoteid) {
		return db.getMemberByMail(remoteid) != null;
	}

}
